
package com.epam.android.widget;

import android.content.Context;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout.LayoutParams;
import android.widget.SeekBar;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CheckpointSeekBar extends SeekBar {

  public interface OnCheckpointSeekBarChangeListener {
    void onCheckPointReached(int reachedCheckPoint, int progressAfterCheckpoint);

    void onNotPassedCheckPointReached(int notPassedCheckpointNumber);

    void onProgressChanged(int reachedCheckPoint, int progressAfterCheckpoint, boolean fromUser);
  }

  private OnCheckpointSeekBarChangeListener checkpointSeekBarChangeListener;

  List<Integer> checkpoints;

  Integer notPassedCheckpointNumber = 0;

  int nextCheckpointNumber = 0;

  boolean fromUser;

  private OnSeekBarChangeListener innerSeekBarChangeListener = new OnSeekBarChangeListener() {

    @Override
    public void onProgressChanged(final SeekBar seekBar, final int progress, final boolean fromUser) {
      // Log.i("D", "hu" + progress + fromUser);
      if (fromUser) {
        // TODO: synchronization
        CheckpointSeekBar.this.fromUser = true;
        setProgress(progress);
      }
    }

    @Override
    public void onStartTrackingTouch(final SeekBar seekBar) {
      if (seekBarChangeListener != null) {
        seekBarChangeListener.onStartTrackingTouch(seekBar);
      }
    }

    @Override
    public void onStopTrackingTouch(final SeekBar seekBar) {
      if (seekBarChangeListener != null) {
        seekBarChangeListener.onStopTrackingTouch(seekBar);
      }
    }

  };

  private int mWidthMeasureSpec;

  private int firstCheckpointViewIndex = -1;

  private OnSeekBarChangeListener seekBarChangeListener;

  public CheckpointSeekBar(final Context context, final AttributeSet attrs) {
    this(context, attrs, android.R.attr.seekBarStyle);
  }

  public CheckpointSeekBar(final Context context, final AttributeSet attrs, final int defStyle) {
    super(context, attrs, defStyle);
    super.setOnSeekBarChangeListener(innerSeekBarChangeListener);
  }

  public void setCheckpoints(final List<Integer> checkpoints) {
    this.checkpoints = new ArrayList<Integer>(checkpoints);
    Collections.sort(checkpoints);
    if (getWidth() != 0) {
      // TODO: analyze race conditions possibility
      drawCheckoints();
    } // else it will be called from onLayout()
  }

  @Override
  protected void onLayout(final boolean changed, final int left, final int top, final int right, final int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    if (changed && checkpoints != null) {
      drawCheckoints();
    }
  }

  void drawCheckoints() {
    LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    ViewGroup parent = (ViewGroup) getParent();
    Log.i("D", firstCheckpointViewIndex + "-" + checkpoints + "!");
    if (firstCheckpointViewIndex == -1) {
      firstCheckpointViewIndex = parent.getChildCount();
    } else {
      parent.removeViews(firstCheckpointViewIndex, checkpoints.size());
    }
    for (int i = 0; i < checkpoints.size(); i++) {
      View child = new Button(getContext());
      // TODO: move background settings to layout xml
      if (i < notPassedCheckpointNumber) {
        // child = inflater.inflate(R.layout.passed_checkpoint, null);
        child.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
      } else {
        // child = inflater.inflate(R.layout.not_passed_checkpoint, null);
        child.getBackground().setColorFilter(0xFFFF0000, PorterDuff.Mode.MULTIPLY);
      }
      int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec, 0, LayoutParams.WRAP_CONTENT);
      int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
      child.measure(childWidthSpec, childHeightSpec);
      int childLeft = (getWidth() * checkpoints.get(i)) / getMax() - child.getMeasuredWidth();

      LayoutParams param = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
      param.gravity = Gravity.TOP;
      param.setMargins(childLeft, 0, 0, 0);
      parent.addView(child, firstCheckpointViewIndex + i, param);
      child.layout(childLeft, 0, childLeft + child.getMeasuredWidth(), child.getMeasuredHeight());
      Log.i("D", parent.getChildCount() + "?" + childLeft);
    }
  }

  @Override
  protected synchronized void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    mWidthMeasureSpec = widthMeasureSpec;
  }

  @Override
  public synchronized void setProgress(int progress) {
    if (checkpoints != null) {
      // Log.i("D", notPassedCheckpointNumber + "-" +
      // checkpoints.get(notPassedCheckpointNumber) + "-" + progress);
      if (notPassedCheckpointNumber > -1 && progress >= checkpoints.get(notPassedCheckpointNumber)) {
        progress = checkpoints.get(notPassedCheckpointNumber);
        if (checkpointSeekBarChangeListener != null) {
          Log.i("D", checkpoints.get(notPassedCheckpointNumber) + "!");
          checkpointSeekBarChangeListener.onNotPassedCheckPointReached(notPassedCheckpointNumber);
        }
      } else if (progress >= checkpoints.get(nextCheckpointNumber)
          || (nextCheckpointNumber > 0 && progress <= checkpoints.get(nextCheckpointNumber - 1))) {
        Log.i(
            "D",
            nextCheckpointNumber + "pop" + progress + "pip"
                + (nextCheckpointNumber > 0 ? checkpoints.get(nextCheckpointNumber - 1) : "hui"));
        nextCheckpointNumber = checkpoints.indexOf(progress) + 1;
        // if progress hasn't exactly matched checkpoint we will search for
        // next checkpoint
        if (nextCheckpointNumber == 0) {
          List<Integer> subList = checkpoints.subList(nextCheckpointNumber, checkpoints.size());
          subList.add(progress);
          Collections.sort(subList);
          nextCheckpointNumber = checkpoints.indexOf(progress);
          checkpoints.remove(nextCheckpointNumber);
        }
        if (checkpointSeekBarChangeListener != null) {
          Integer reachedCheckpoint = 0;
          if (nextCheckpointNumber > 0) {
            reachedCheckpoint = checkpoints.get(nextCheckpointNumber - 1);
          }
          checkpointSeekBarChangeListener.onCheckPointReached(reachedCheckpoint, progress - reachedCheckpoint);
        }
      }
    }
    Log.i("D", "pad" + nextCheckpointNumber);
    super.setProgress(progress);
    if (checkpointSeekBarChangeListener != null) {
      Integer reachedCheckpoint = 0;
      if (checkpoints != null && nextCheckpointNumber > 0) {
        reachedCheckpoint = checkpoints.get(nextCheckpointNumber - 1);
      }
      checkpointSeekBarChangeListener.onProgressChanged(reachedCheckpoint, progress - reachedCheckpoint, fromUser);
    }
    if (seekBarChangeListener != null) {
      seekBarChangeListener.onProgressChanged(this, progress, fromUser);
    }
    fromUser = false;
  }

  public Integer getNotPassedCheckpointNumber() {
    return notPassedCheckpointNumber;
  }

  public void setNotPassedCheckpointNumber(final Integer notPassedCheckpointNumber) {
    this.notPassedCheckpointNumber = notPassedCheckpointNumber;
  }

  public void setOnCheckpointSeekBarChangeListener(
      final OnCheckpointSeekBarChangeListener checkpointSeekBarChangeListener) {
    this.checkpointSeekBarChangeListener = checkpointSeekBarChangeListener;
  }

  @Override
  public void setOnSeekBarChangeListener(final OnSeekBarChangeListener l) {
    seekBarChangeListener = l;
  }

  public void setProgressAfterCheckpoint(final int progress) {
    if (checkpoints != null && nextCheckpointNumber > 0) {
      setProgress(checkpoints.get(nextCheckpointNumber - 1) + progress);
    } else {
      setProgress(progress);
    }
  }

  public void passCheckpoint() {
    ((ViewGroup) getParent()).getChildAt(firstCheckpointViewIndex + notPassedCheckpointNumber).getBackground()
        .setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
    if (notPassedCheckpointNumber < checkpoints.size() - 1) {
      ++notPassedCheckpointNumber;
    } else {
      notPassedCheckpointNumber = -1;
    }
    Log.i("D", "passcheck" + notPassedCheckpointNumber);
    setProgress(getProgress());
  }
}
